package priv.wangy.micro.common.utils;

import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author wangy
 * @since 2023-11-08
 */
@Slf4j
public class DateUtil {

    public static final String DATE_Y_M = "yyyy-MM";

    public static final String DATE = "yyyy-MM-dd";

    public static final String DATE_ZH = "yyyy年MM月dd日";

    public static final String DATETIME = "yyyy-MM-dd HH:mm:ss";

    public static final String DATETIME_ZH = "yyyy年MM月dd日 HH时mm分ss秒";

    public static final String DATETIME_MILLISECOND = "yyyy-MM-dd HH:mm:ss.SSS";

    /**
     *
     * 字符串转Date
     * @author wangy
     * @since 9:40 2024/7/30
     * @param dateTime 时间
     * @param pattern 字符串格式
     * @return java.util.Date
     **/
    public static Date stringToDate(String dateTime, String pattern) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        return sdf.parse(dateTime);
    }

    public static Date stringToDateNoThrow(String dateTime, String pattern) {
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        try {
            return sdf.parse(dateTime);
        } catch (ParseException e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return null;
    }


    public static boolean isAfterMinutes(LocalTime time, int minutes) {
        if (minutes <= 0) {
            minutes = Integer.MAX_VALUE;
        }
        LocalTime now = LocalTime.now();
        // 比较两个时间是否相差超过一分钟
        return Math.abs(now.getMinute() - time.getMinute()) > minutes;
    }

    /**
     * 判断是否在开始日期之后
     *
     * @param startDate 开始日期
     * @return 当前时间晚于开始日期时返回true
     */
    public static boolean isAfter(Date startDate) {
        // 获取当前时间
        LocalDate currentTime = LocalDate.now();
        // 定义开始时间和结束时间
        LocalDate endTime = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate().minusDays(1);
        // 判断当前时间是否在范围内
        return currentTime.isAfter(endTime);

    }

    public static boolean isBefore(Date startDate) {
        // 获取当前时间
        LocalDate currentTime = LocalDate.now();
        LocalDate startTime = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        return currentTime.isBefore(startTime);
    }

    public static boolean isBefore(String startDate) {
        if (!StringUtils.hasLength(startDate)) {
            return false;
        }
        LocalDate currentTime = LocalDate.now();
        LocalDate startTime = LocalDate.parse(startDate);
        return currentTime.isBefore(startTime);
    }

    /**
     * 判断当前时间是否在两个日期中间
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 比较结果，当前时间在开始日期与结束日期之间时返回true
     */
    public static boolean isBetween(Date startDate, Date endDate) {
        // 获取当前时间
        LocalDate currentTime = LocalDate.now();
        // 定义开始时间和结束时间
        LocalDate startTime = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate().minusDays(1);
        LocalDate endTime = endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        // 判断当前时间是否在范围内
        return currentTime.isAfter(startTime) && currentTime.isBefore(endTime);
    }

    public static boolean isToday(Date date) {
        LocalDate now = LocalDate.now();
        // 定义开始时间和结束时间
        LocalDate date1 = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        return now.isEqual(date1);
    }


    /**
     * 计算某一 日期之后 x天的日期
     *
     * @param date
     * @param days
     * @return
     */
    public static Date afterDay(Date date, int days) {
        if (days <= 0) {
            days = Integer.MAX_VALUE;
        }
        LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        LocalDate afterMonth = localDate.plusDays(days);
        return Date.from(afterMonth.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     *计算某一 日期之后 x年的日期
     * @param date
     * @param year
     * @return
     */
    public static Date afterYear(Date date, int year) {
        if (year <= 0) {
            year = 50;
        }
        LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        LocalDate afterYear = localDate.plusYears(year);
        return Date.from(afterYear.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 计算某一 日期之后 x月的日期
     * @param date
     * @param month
     * @return
     */
    public static Date afterMonth(Date date, int month) {
        LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        LocalDate afterMonth = localDate.plusMonths(month);
        return Date.from(afterMonth.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 计算某一 日期之后 x周的日期
     * @param date
     * @param week
     * @return
     */
    public static Date afterWeek(Date date, int week) {
        LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        LocalDate afterWeek = localDate.plusWeeks(week);
        return Date.from(afterWeek.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 获取当前时间 前一周周几 的时间
     *
     * @param date 时间
     * @param week 周几
     * @return LocalDate
     */
    public static LocalDate beforeWeek(String date, String pattern, DayOfWeek week) {
        LocalDate currentDate = LocalDate.parse(date, DateTimeFormatter.ofPattern(pattern));
        return currentDate.minusWeeks(1).with(TemporalAdjusters.previousOrSame(week));
    }

    /**
     *
     * 时间戳转字符串工具
     *
     * @author wangy
     * @since 14:45 2024/7/3
     * @param timestamp
     * @return java.lang.String
     **/
    public static String timestampToString(long timestamp, String formatStr) {
        Date date = new Date(timestamp);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatStr);
        return dateToString(date, formatter);
    }


    /**
     * date转String
     * @author wangy
     * @since 14:47 2024/7/3
     * @param date 日期
     * @param formatter 格式
     * @return java.lang.String
     **/
    public static String dateToString(Date date, DateTimeFormatter formatter) {
        LocalDateTime time = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
        return time.format(formatter);

    }

    /**
     * 获取当前时间 前一周周几 的时间
     *
     * @param currentDate 时间
     * @param week        周几
     * @return LocalDate
     */
    public static LocalDate beforeWeek(LocalDate currentDate, DayOfWeek week) {
        return currentDate.minusWeeks(1).with(TemporalAdjusters.previousOrSame(week));
    }

    /**
     * 获取两个日期间的日期列表
     *
     * @param start   开始日期
     * @param end     结束日期
     * @param pattern 响应的日期格式
     * @return java.util.List<java.lang.String>
     * @author zhangjq
     * @since 2024-05-27
     */
    public static List<String> rangeDates(LocalDate start, LocalDate end, String pattern) {
        List<String> result = new ArrayList<>();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        while (!start.isAfter(end)) {
            result.add(start.format(formatter));
            start = start.plusDays(1);
        }
        return result;
    }

    /**
     * 获取两个日期间的日期列表
     *
     * @param start   开始日期，时间戳，milliseconds
     * @param end     结束日期，时间戳，milliseconds
     * @param pattern 响应的日期格式
     * @return java.util.List<java.lang.String>
     * @author zhangjq
     * @since 2024-05-27
     */
    public static List<String> rangeDates(long start, long end, String pattern) {
        LocalDate startDate = LocalDate.ofInstant(Instant.ofEpochMilli(start), ZoneId.systemDefault());
        LocalDate endDate = LocalDate.ofInstant(Instant.ofEpochMilli(end), ZoneId.systemDefault());
        return rangeDates(startDate, endDate, pattern);
    }

    /**
     * 获取两个日期间的日期列表
     *
     * @param start   开始日期，格式为yyyy-MM-dd
     * @param end     结束日期，格式为yyyy-MM-dd
     * @param pattern 响应的日期格式
     * @return java.util.List<java.lang.String>
     * @author zhangjq
     * @since 2024-05-27
     */
    public static List<String> rangeDates(String start, String end, String pattern) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate startDate = LocalDate.parse(start, formatter);
        LocalDate endDate = LocalDate.parse(end, formatter);
        return rangeDates(startDate, endDate, pattern);
    }

    /**
     * 获取两个日期间的日期列表
     *
     * @param start      开始日期
     * @param end        结束日期
     * @param inPattern  入参（start、end）的日期格式
     * @param outPattern 响应的日期格式
     * @return java.util.List<java.lang.String>
     * @author zhangjq
     * @since 2024-05-27
     */
    public static List<String> rangeDates(String start, String end, String inPattern, String outPattern) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(inPattern);
        LocalDate startDate = LocalDate.parse(start, formatter);
        LocalDate endDate = LocalDate.parse(end, formatter);
        return rangeDates(startDate, endDate, outPattern);
    }

    /**
     * 获取两个日期间的周列表
     * 若结束日期为周一，该周忽略不计
     * 如结束日期为2024年7月1日(周一)，则结果中的最后一周为2024年6月24日至2024年6月30日
     *
     * @param start 开始日期
     * @param end   结束日期
     * @return java.util.List<com.whminwei.common.utils.DateUtil.WeekBo>
     * @author zhangjq
     * @since 2024-06-25
     */
    public static List<WeekBo> rangeWeeks(LocalDate start, LocalDate end) {
        List<WeekBo> weeks = new ArrayList<>();
        if (start.isBefore(end)) {
            int i = 1;
            LocalDate monday = start.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
            LocalDate sunday = start.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
            while (monday.isBefore(end)) {
                weeks.add(WeekBo.builder().idx(i).monday(monday).sunday(sunday).build());
                monday = monday.plusWeeks(1);
                sunday = sunday.plusWeeks(1);
                i++;
            }
        }
        return weeks;
    }

    @Data
    @Builder
    public static class WeekBo {
        /**
         * 周序号
         */
        private int idx;
        /**
         * 周一
         */
        private LocalDate monday;
        /**
         * 周日
         */
        private LocalDate sunday;
    }
}
